/*	
 * Authors: Ken YANG <spng.yang@gmail.com>
 * 	
 * Introduction
 * Implementation of mmh_server_run(), see details below
 *
 * Copyright (C) 2007-2008 Ken YANG
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation, version 2. 
 * THIS SOFTWARE COMES "AS IS" WITHOUT ANY WARRANTIES.
 */

#include "mmh.h"

/*
 * Name: sig_int()
 *
 * Description: 
 * SIGINT singnal disposition
 *
 * Parameters: 
 * signal number
 *
 * Return Values: 
 * None
 *
 * */
static void sig_int(int signo)
{
	/* 
	 * you can do something here, i.e. the resource 
	 * usage summary through getrusage();
	 * */
	free(tpoll);
	exit(0);
}


/*
 * Name: conn_handle()
 *
 * Description: 
 * Called by each thread, and process the connect request
 *
 * Parameters: 
 * Empty temporarily
 *
 * Return Values:
 * None
 *
 * */
void *
conn_handle(void *arg)
{
	int connfd;
	socketlen_t clilen;
	struct sockaddr *cliaddr;

	cliaddr=Malloc(mmh_addrlen);

	for(;;){
		clilen=mmh_addrlen;
		Pthread_mutex_lock(&mmh_accept_lock);
		connfd=Accept(mmh_listenfd,cliaddr,&clilen);
		Pthread_mutex_unlock(&mmh_accept_lock);
		tpoll[(int)arg].conn_handle++;
		/* 
		 * TODO
		 * call certain func here to do real stuffs.
		 * Now left empty temporarily
		 * */
		Close(connfd);
	}
	free(cliaddr);
}


/*
 * Name: create_thread_pool()
 *
 * Description: 
 * Create thread poll which have "num" threads
 *
 * Parameters: 
 * the number of threads
 *
 * Return Values: 
 * None
 *
 * */
static void create_thread_pool(int num)
{
	/* thread function to handle arrived connection request */
	void *conn_handle(void *);
	Pthread_create(tpoll[num],NULL,conn_handle,(void *)num);
}


/*
 * Name: mmh_server_run()
 *
 * Description:
 * Take care of server stuffs before sending data, including 
 * create socket, set socket options, bind, connect and 
 * etc...
 *
 * FIXME:
 * My aim is to be independent of IPv4 and IPv6, but still 
 * had not test environment
 *
 * Parameters:
 * host: 	Local hostname or ip addr representation
 * service:	service or port number
 *
 * Return Values:
 * The server socket 
 *
 * */
int 
mmh_server_run(char *host,char *service)
{
	int serv_sockfd, rc;
	struct addrinfo hint, *result, *index;

	bzero(&hint, sizeof(struct addrinf));
	hint.ai_socktype=mmh_udp_f ? SOCK_DGRAM : SOCK_STREAM;
	hint.ai_family=AF_UNSPEC;

	if((rc==getaddrinfo(host,service,&hint,&result))!=0)
		err_quit(gai_error(rc));

	index=result;
	do{
		/* socket error here isnt fatal */
		serv_sockfd=socket(index->ai_family,
				mmh_udp_f?SOCK_DGRAM:SOCK_STREAM,0);
		if(serv_sockfd<0) continue;	/* try next one */
		Setsockopt(serv_sockfd,SOL_SOCKET,SO_REUSEADDR,1,sizeof(int));
#ifdef SO_REUSEPORT
		Setsockopt(serv_sockfd,SOL_SOCKET,SO_REUSEPORT,1,sizeof(int));
#endif
		if(bind(serv_sockfd,index->ai_addr,index->ai_addrlen)==0)
			break;	/* success */
		Close(serv_sockfd);
	}while((index=index->ai_next)!=NULL);

	if(mmh_udp_f){
		mmh_buffers_alloc(serv_sockfd);
		if(mmh_foreign[0]!=0){
			bzero(&mmh_cliaddr,sizeof(mmh_cliaddr));
			mmh_cliaddr.sin_family=index->ai_family;
			mmh_cliaddr.sin_port=htons(mmh_foreignport);
			Inet_pton(index->ai_family,mmh_foreign,
					&mmh_cliaddr.sin_addr);
			/* connect for DATAGRAM socket */
			Connect(serv_sockfd,&mmh_cliaddr,
					sizeof(mmh_cliaddr));
		}
		
		mmh_sockopts(serv_sockfd,1);
		/* UDP server finish here */
		return(serv_sockfd);
	}

	/* 
	 * For TCP, may set socket received buffer size. 
	 * Must do here to get correct window size 
	 * advertised on SYN 
	 * */
	mmh_buffers_alloc(serv_sockfd);
	/* 
	 * Only set some options, because some options can 
	 * not set currently 
	 * */
	mmh_sockopts(serv_sockfd,0);

	Listen(serv_sockfd, mmh_listen_queue);

	if(mmh_concurrent){
		/* Concurrent server, using threads poll */
		int i;
		Sigfunc *sig_int;
		Signal(SIGINT,sig_int);
		Signal(SIGKILL,sig_int);
		Signal(SIGSTOP,sig_int);
		tpoll=Calloc(mmh_nthreads,sizeof(struct thread_poll_t));
		for(i=0;i<ntrehads;i++)
			create_thread_pool(i);
		for(;;)
			pause();	/* everything done by threads */
	}else{
		/* Iterative server */
		/* TODO */
	}


	return(serv_sockfd);
}



